#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sys/stat.h>
#include <experimental/filesystem>
#include <jsoncpp/json/json.h>
#include "bundle/bundle.h"


namespace shy
{
  namespace fs = std::experimental::filesystem;
  class FileUtil
  {
    private:
      std::string _filename;
    public:
      FileUtil(const std::string& filename)
        :_filename(filename)
      {} // structure function 

      bool Remove()
      {
        remove(_filename.c_str());
        return true;
      }
      int64_t FileSize()
      {
        struct stat st; 
        int ret = stat(_filename.c_str(), &st);
        if (ret < 0) 
        {
          std::cout << "Get file size failed!" << std::endl;
          return -1;
        }

        return st.st_size;
      }

      time_t LastModifyTime()
      {
        struct stat st;
        int ret = stat(_filename.c_str() , &st);
        if (ret < 0)
        {
          std::cout << "Get lastmodifytime failed!" << std::endl;
          return -1;
        }

        return st.st_mtime;
      }


      time_t LastAccessTime()
      {
        struct stat st;

        int ret = stat(_filename.c_str() , &st);
        if (ret < 0)
        {
          std::cout << "Get lastaccesstime failed!"  << std::endl;
          return -1;
        }

        return st.st_atime;
      }

      std::string FileName()
      {
        std::experimental::filesystem::path p(_filename);
        return p.filename().string();
      }

      bool GetPosLen(std::string& body , size_t pos , size_t len)
      {
        size_t fsize = this->FileSize();
        if (pos + len < fsize)
        {
          std::cout << "Get file len failed!" << std::endl;
          return false;
        }

        std::ifstream ifs;
        ifs.open(_filename.c_str() , std::ios::binary);
        if (ifs.is_open() == false)
        {
          std::cout << "open file failed!" <<  std::endl;
          return false;
        }

        ifs.seekg(pos , std::ios::beg);
        body.resize(len);
        ifs.read(&body[0] , len);
        if (ifs.good() == false)
        {
          std::cout << "read file failed!" << std::endl;
          return false;
        }
        ifs.close();
        return true;
      }

      bool GetContent(std::string& body)
      {
        size_t fsize = this->FileSize();
        return this->GetPosLen(body , 0 , fsize);
      }

      bool SetContent(const std::string& body)
      {
        std::ofstream ofs;
        ofs.open(_filename.c_str() , std::ios::binary);
        if (ofs.is_open() == false)
        {
          std::cout << "open ofs failed" << std::endl;
          return false;
        }

        ofs.write(&body[0] , body.size());
        if (ofs.good() == false)
        {
          std::cout << "ofs write failed!" << std::endl;
          return false;
        }
        ofs.close();
        return true;
      }

      bool Compress(const std::string &packname)
      {
        // 1. get source date 
        std::string body;
        if (this->GetContent(body) == false)
        {
          std::cout << "compress get file content failed" << std::endl;
          return false;
        }

        // 2. compress date 
        std::string packed = bundle::pack(bundle::LZIP , body);

        // 3 write comress date to packname 
        FileUtil fu(packname); 
        if (fu.SetContent(packed) == false)
        {
          std::cout << "write compress date failed" << std::endl;
          return false;
        }

        return true;
      }


      bool uncompress(std::string& unpackname)
      {
        // 1. get compress file name 
        std::string body;
        if (this->GetContent(body) == false)
        {
          std::cout << "get Compress file content failed" << std::endl;
          return false;
        }

        // 2 . uncompress file 
        std::string unpacked = bundle::unpack(body);

        // 3. write unpacked file 
        FileUtil fu(unpackname); 
        if (fu.SetContent(unpacked) == false)
        {
          std::cout << "uncompress write unpacked file failed" << std::endl;
          return false;
        }

        return true;
      }

      bool Exists()
      {
        return fs::exists(_filename);
      }

      bool CreateDirectory()
      {
        if (this -> Exists())
        {
          return true;
        }

        return fs::create_directories(_filename);
      }

      bool ScanDirectory(std::vector<std::string>& arry)
      {
        for (auto& p : fs::directory_iterator(_filename))
        {
          if (fs::is_directory(p) == true)
          {
            continue;
          }

          // return relative path
          arry.push_back(fs::path(p).relative_path().string());
        }
        return true;
      }
  };


  class jsonUtil
  {
    public:
      static bool Serialize(const Json::Value& root , std::string& str)
      {
         Json::StreamWriterBuilder swb;    
         std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());       
         std::stringstream ss;     
         sw->write(root , &ss); 
         str = ss.str();
         return true;
      }

      static bool UnSerialize(const std::string& str , Json::Value& root)
      {
           Json::CharReaderBuilder crb;  // subclass    
           std::unique_ptr<Json::CharReader> cr(crb.newCharReader());     
           std::string err;    
           bool set = cr->parse(str.c_str() , str.c_str()+str.size() , &root , &err);   
           if (set == false)
           {
             return false;
           }

           return true;
      }
  };
  }
